/**
 * @file PlayState.h
 *
 * Estado de juego.
 */

#ifndef __PlayState_h__
#define __PlayState_h__

#include <cstdlib>
#include <time.h>
#include <vector>
#include <list>
#include <iterator>
#include <Ogre.h>
#include <OIS/OIS.h>

#include <GameState.h>
#include "RecordManager.h"

/// Ancho (en cuadraditos) de la escena.
#define SCENEWIDTH 10

/// Largo (en cuadraditos) de la escena.
#define SCENEHEIGHT 30

/// Tiempo que tarda la pieza en bajar un cuadradito.
#define TIMETOFALL 0.8

/// Tiempo mínimo de respuesta (debe ser divisor de TIMETOFALL).
#define TIMEMIN 0.1

/// Tiempo entre los parpadeos de una línea al ser borrada.
#define TIMEFORBLINK 0.5

// Colores 

#define YELLOW	1
#define ORANGE	2
#define GREEN		3
#define RED			4
#define CYAN		5
#define BLUE		6
#define MAGENTA	7

// Multiplicador de puntuación
#define SCOREMULT 10

/// Define una posición.
struct Position
{
  int x; ///< Posición en el eje x.
  int y; ///< Posición en el eje y.
};

/// Estado de juego.
class PlayState: public Ogre::Singleton<PlayState>, public GameState
{
  public:
    /// Constructor.
    PlayState();

    /// Destructor.
    ~PlayState() throw();

    /// Se ejecuta al iniciar este estado.
    void enter();

    /// Se ejecuta al salir de este estado.
    void exit();

    /// Se ejecuta al pausar este estado.
    void pause();

    /// Se ejecuta al volver de una pausa desde otro estado.
    void resume();

    /**
     * Detecta cuando se ha pulsado una tecla.
     *
     * @param[in] e Tecla pulsada.
     */
    void keyPressed(const OIS::KeyEvent& e);

    /**
     * Detecta cuando se ha soltado una tecla.
     *
     * @param[in] e Tecla soltada.
     */
    void keyReleased(const OIS::KeyEvent& e);

    /**
     * Detecta cuando se ha movido el ratón.
     *
     * @param[in] e Evento del ratón.
     */
    void mouseMoved(const OIS::MouseEvent& e);

    /**
     * Detecta cuando se ha pulsado un botón del ratón.
     *
     * @param[in] e  Evento del ratón.
     * @param[in] id Botón pulsado.
     */
    void mousePressed(const OIS::MouseEvent& e, OIS::MouseButtonID id);

    /**
     * Detecta cuando se ha soltado un botón del ratón.
     *
     * @param[in] e  Evento del ratón.
     * @param[in] id Botón soltado.
     */
    void mouseReleased(const OIS::MouseEvent& e, OIS::MouseButtonID id);

    /**
     * Se ejecuta cuando se va a renderizar un frame.
     *
     * @param[in] evt Estructura con información acerca del frame.
     */
    virtual bool frameStarted(const Ogre::FrameEvent& evt);

    /**
     * Se ejecuta cuando se ha terminado de renderizar un frame.
     *
     * @param[in] evt Estructura con información acerca del frame.
     */
    virtual bool frameEnded(const Ogre::FrameEvent& evt);

    // Heredados de Ogre::Singleton.

    /// Devuelve una referencia a este singleton.
    static PlayState& getSingleton();

    /// Devuelve un puntero a este singleton.
    static PlayState* getSingletonPtr();

    // Para el tetris

  private:
    /// Inicializa las piezas que se van a usar.
    void initPieces();

    /// Genera la pieza al principio.
    bool generate();

    /// Rota la pieza.
    void rotate();

    /**
     * Mueve la pieza lateralmente.
     *
     * @param[in] y Número de cuadraditos a mover en horizontal.
     */
    void move(const int y);

    /// Baja la pieza.
    void fall();

    /// Empotra la pieza en la cuadrícula (cuando no se puede mover más).
    void save();
    
    /// Dibuja la cuadrícula
    void drawScene();
    
    /// Genera el nodo que representa los limites del escenario
    Ogre::SceneNode* buildRectangle();
    
    /// Devuelve una string con el color de un cubo
    std::string getCubeColor(const int);

    /// Devuelve true si una linea esta completa
    bool completeLine(std::vector<int>);

    /// Busca lineas completas y actualiza la puntuacion
    void searchCompleteLines();

  protected:
    Ogre::Root* _root;             ///< Nodo root.
    Ogre::SceneManager* _sceneMgr; ///< SceneManager.
    Ogre::Viewport* _viewport;     ///< Viewport.
    Ogre::Camera* _camera;         ///< Cámara.

    Ogre::Real _timeToFall; ///< Para llevar la cuenta del tiempo.
    Ogre::Real _timeMin;    ///< Para llevar la cuenta del tiempo.

    std::list<std::vector<int> > _scene; ///< Cuadrícula que representa el tablero.
    Ogre::SceneNode* _sceneNode; ///< Nodo con la representación del tablero.
    Ogre::SceneNode* _rectangleNode; ///< Nodo con el contorno del tablero.

    /// Piezas del tetris.
    std::vector< std::vector< std::vector<Position> > > _pieces; 
    
    std::vector<std::vector<Position> >*
      _currentPieceType;             ///< Vector de posiciones de la pieza actual.
    std::vector<Position>*
      _currentPiece;                 ///< Pieza actual.
    int _rotation;                   ///< Índice de la pieza actual.
    Position _currentPiecePosition;  ///< Posición de la pieza actual.
    int _pieceColor;                 ///< Color de la pieza.
    
    int _score; ///< Puntuacion

    int _yoffset; ///< Desplazamiento para mover la pieza en horizontal.

    bool _rotatePiece; ///< True cuando hay que rotar la pieza.
    bool _rapidFall;   ///< True cuando hay que descender rápidamente la pieza.
    bool _getNewPiece; ///< True cuando hay que generar una nueva pieza.

    /// True cuando toca redibujar en una iteración del bucle principal.
    bool _drawInThisIteration;

    bool _exitGame; ///< True cuando ha terminado este estado.

    TrackManager* _pTrackManager;     ///< TrackManager de SDL.
    SoundFXManager* _pSoundFXManager; ///< SoundFXManager de SDL.
};

#endif
